package util.xml;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import java.io.FileInputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * xml 工具类
 * @Author zt
 * @date 2021/11/11 16:16
 * @Version 1.0
 */
public class XmlUtil {
    /**
     * JavaBean转换成xml 默认编码UTF-8
     *
     * @param obj 源数据
     * @return 结果
     */
    public static String convertToXml(Object obj) {
        return convertToXml(obj, "UTF-8");
    }

    /**
     * JavaBean转换成xml
     *
     * @param obj 源数据
     * @param encoding 编码
     * @return 结果
     */
    public static String convertToXml(Object obj, String encoding, boolean fragment) {
        String result = null;
        try {
            JAXBContext context = JAXBContext.newInstance(obj.getClass());
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, fragment);
            marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);

            StringWriter writer = new StringWriter();
            marshaller.marshal(obj, writer);
            result = writer.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

    /**
     * JavaBean转换成xml
     *
     * @param obj 源数据
     * @param encoding  编码
     * @return 结果
     */
    public static String convertToXml(Object obj, String encoding) {
        String result = null;
        try {
            JAXBContext context = JAXBContext.newInstance(obj.getClass());
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.FALSE);
            marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);

            StringWriter writer = new StringWriter();
            marshaller.marshal(obj, writer);
            result = writer.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

    /**
     * xml转换成JavaBean
     *
     * @param xml 源数据
     * @param c 目标类
     * @return 结果
     */
    @SuppressWarnings("unchecked")
    public static <T> T converyToJavaBean(String xml, Class<T> c) {
        T t = null;
        try {
            JAXBContext context = JAXBContext.newInstance(c);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            t = (T) unmarshaller.unmarshal(new StreamSource(new StringReader(xml)));
        } catch (Exception e) {
            e.printStackTrace();
        }

        return t;
    }

    /**
     * 根据标签名查看是否含有某个标签的标签体
     *
     * @param labelName 标签名
     * @param labelStr 数据源
     * @return 结果
     */
    private static boolean isContainLabName(String labelStr,String labelName) {
        if (labelStr == null) {
            return false;
        }
        labelStr = labelStr.trim();
        int length = labelStr.length();
        if (length <= 0) {
            return false;
        }
        int startIndex = labelStr.indexOf("<"+ labelName+" ");
        int endIndex = labelStr.indexOf("</" + labelName+">");
        int endLabelIndex = labelStr.indexOf("/>");
        if (startIndex >=0 && ((endLabelIndex<=length && endLabelIndex >0)||(endIndex>0 && endIndex <=length)) ) {
            return true;
        }
        return false;
    }

    /**
     * 从xml字符串中根据标签名 获取标签字符串
     *
     * @param xmlStr  数据源
     * @param labelName 标签名
     * @return 结果
     */
    private static List<String> getLabelStrLst(String xmlStr, String labelName) {
        String [] xmlStrArr = xmlStr.split("\n");
        List <String> xmlStrLst = Arrays.asList(xmlStrArr);
        List<String> labelStrLst = new ArrayList<String>();
        for (String labelStr : xmlStrLst) {
            if (isContainLabName(labelStr, labelName)) {
                labelStrLst.add(labelStr.trim());
            }
        }
        return labelStrLst;
    }

    /**
     * 根据xml片段获取 某个标签的所有属性及其对应字符串
     * @param xmlStr  xml片段字符串 或者xml字符串
     * @param  rule 表达式
     * @param labelName 标签名
     * @return 结果
     */
    private static List<Map<String,String>> getKeyValMapLstByXmlStrAndLabelName(String xmlStr, String labelName,String rule) {
        List<Map<String,String>> keyValMapLst = new ArrayList<Map<String,String>>();
        List<String> labelStrLst = getLabelStrLst(xmlStr, labelName);
        Map<String,String> keyValMap = null;
        for (String labelStr : labelStrLst) {
            keyValMap = getKeyValOfXmlStr(labelStr,rule);
            keyValMapLst.add(keyValMap);
        }
        return keyValMapLst;
    }

    /**
     * 根据文件名 和 标签名 在xml文件中获取所有标签内的键值对字符串
     *
     * @param fileName 文件名
     * @param labelName 标签名
     * @param rule 表达式
     * @return 结果
     * @throws Exception  异常
     */
    private static List<Map<String,String>> getKeyValMapLstByFileNameAndLabelName(String fileName,String labelName,String rule) throws Exception {
        String xmlStr = readFile(fileName);
        List<Map<String,String>> mapLst = getKeyValMapLstByXmlStrAndLabelName(xmlStr, labelName, rule);
        return mapLst;
    }

    /**
     * 根据文件路径 和 标签名 在xml文件中获取所有标签内的键值对字符串
     * @param filePath 文件路径
     * @param labelName 文件名
     * @param rule 表达式
     * @return 结果
     * @throws Exception 异常
     */
    public static List<Map<String,String>> getKeyValMapLstByFilePathAndLabelName(String filePath,String labelName,String rule) throws Exception {
        String fileName = getProjectPath(filePath);
        List<Map<String,String>> mapLst = getKeyValMapLstByFileNameAndLabelName(fileName,labelName,rule);
        return mapLst;
    }


    /**
     * 读取文件内容
     * @param path 文件在项目中的路径
     * @return 结果
     * @throws Exception 异常
     */
    public static String readFile(String path) throws Exception {
        File file=new File(path);
        FileInputStream fis = new FileInputStream(file);
        FileChannel fc = fis.getChannel();
        ByteBuffer bb = ByteBuffer.allocate(new Long(file.length()).intValue());
        //fc向buffer中读入数据
        fc.read(bb);
        bb.flip();
        String str=new String(bb.array(),"UTF8");
        fc.close();
        fis.close();
        return str;
    }


    /**
     * 根据相对路劲获取绝对路劲
     * @param  relativePath 数据源
     * @return 结果
     */
    public static String getProjectPath(String relativePath) {
        try {
            String absolutePath = "";
            if (relativePath == null || "".equals(relativePath.trim())) {
                absolutePath = getRootPath();
            } else {
                if (relativePath.trim().indexOf("/") != 0) {
                    relativePath = "/" + relativePath.trim();
                }
                absolutePath =  XmlUtil.class.getResource(relativePath).getPath();
            }
            return absolutePath;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取项目根路径
     * @return 结果
     */
    public static String getRootPath() {
        try {
            String rootPath = getProjectPath("/");
            return rootPath;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 通过xmlStr 获取满足正则表达式的结果 并存入map中
     * @param xmlStr 数据源
     * @param  rule 表达式
     * @return  能够匹配上的xml字符串
     */
    public static Map<String ,String> getKeyValOfXmlStr(String xmlStr,String rule) {
        Map<String,String> attrMap =  new HashMap<String,String>();
        Pattern p=Pattern.compile(rule);
        Matcher m=p.matcher(xmlStr);
        String group = null;
        String key = null;
        String val = null;
        while(m.find()) {
            group = m.group();
            key = group.substring(0, group.indexOf("="));
            val = group.substring(group.indexOf("\"")+1, group.lastIndexOf("\""));
            attrMap.put(key, val);
        }
        return attrMap;
    }

}
